using System;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Microsoft.ApplicationBlocks.Updater;

namespace SimpleAppStart
{
	///<summary>
	///This is the main class for AppStart.exe.
	///</summary>
	public sealed class AppStart
	{
		#region Private members

		/// <summary>
		/// The configuration for the AppStart process.
		/// </summary>
		private static AppStartConfiguration config = null;

		/// <summary>
		/// The process of the running application.
		/// </summary>
		private static Process applicationProcess;

		/// <summary>
		/// The mutex name for the AppStart.
		/// </summary>
		private static readonly Guid appStartMutexGuid = new Guid( new byte[] { 0x5F, 0x4D, 0x69, 0x63, 0x68, 0x61, 0x65, 0x6C, 0x20, 0x53, 0x74, 0x75, 0x61, 0x72, 0x74, 0x5F });

		#endregion 

		#region Constructors

		/// <summary>
		/// Static constructor.
		/// </summary>
		static AppStart()
		{			
			//  Grab our config instance which we use to read app.config params, figure out
			//  WHERE our target app is and WHAT version
			config = (AppStartConfiguration)System.Configuration.ConfigurationManager.GetSection( "appStart" );
		}

		/// <summary>
		/// No reason to ever "construct" this from outside, meant purely as invisible shim.
		/// </summary>
		private AppStart() {}

		#endregion

		#region Static members

		/// <summary>
		/// Main entry point to process.  Checks to see if another instance is running already, disallows if so.
		/// </summary>
		/// <param name="args">Arguments are ignored.</param>
		[STAThread]
		static void Main(string[] args) 
		{
			//Check to see if AppStart is already running FOR the particular versioned folder of the target application
			bool isOwned = false;

			Mutex appStartMutex = new Mutex( true,  config.ExecutableName + appStartMutexGuid.ToString(), out isOwned );
	
			if ( !isOwned )
			{
				MessageBox.Show( 
					String.Format( 
					CultureInfo.CurrentCulture, 
					"There is already a copy of the application '{0}' running.  Please close that application before starting a new one.", 
					config.ExecutableName ) );

				Environment.Exit( 1 );
			}
			
			StartAppProcess();
		}
				
		/// <summary>
		/// Main process code.
		/// </summary>
		private static void StartAppProcess() 
		{
			bool processStarted = false;

			if ( config.UpdateTime == UpdateTimeEnum.BeforeStart )
			{
				AppStart.UpdateApplication();
			}

			//Start the application
			try 
			{
				ProcessStartInfo p = new ProcessStartInfo (config.ExecutablePath);
				p.WorkingDirectory = Path.GetDirectoryName(config.ExecutablePath);
				applicationProcess = Process.Start (p);
				processStarted = true;
				Debug.WriteLine("APPLICATION STARTER:  Started app:  " + config.ExecutablePath);
			} 
			catch (Exception e)
			{
				Debug.WriteLine("APPLICATION STARTER:  Failed to start process at:  " + config.ExecutablePath);
				HandleTerminalError(e);
			}

			if ( processStarted && config.UpdateTime == UpdateTimeEnum.AfterShutdown )
			{
				applicationProcess.WaitForExit();
				AppStart.UpdateApplication();
			}
		}	

		private static void UpdateApplication()
		{
			ApplicationUpdaterManager updaterManager = ApplicationUpdaterManager.GetUpdater( config.ApplicationID );
			updaterManager.PendingUpdatesDetected +=new PendingUpdatesDetectedEventHandler(updaterManager_PendingUpdatesDetected);
			Manifest[] manifests = updaterManager.CheckForUpdates( config.ManifestUri );
			
			if(manifests.Length > 0)
			{
				if (DialogResult.Yes == MessageBox.Show(null,"Updates available do you want to apply?","SimpleAppStart",MessageBoxButtons.YesNo,MessageBoxIcon.Question) )
				{
					foreach(Manifest manifest in manifests)
					{
						manifest.Application.Location = Path.GetDirectoryName(config.ExecutablePath);
						manifest.Apply = true;
					}
					updaterManager.Download( manifests, TimeSpan.MaxValue );
					updaterManager.Activate( manifests );
				}
			}
			else
			{
				MessageBox.Show(null,"No updates available","SimpleAppStart",MessageBoxButtons.OK,MessageBoxIcon.Information);
			}
		}

		/// <summary>
		/// Prints out the console exception &amp; shuts down the application.
		/// </summary>
		private static void HandleTerminalError(Exception e)
		{
			Debug.WriteLine("APPLICATION STARTER: Terminal error encountered.");
			Debug.WriteLine("APPLICATION STARTER: The following exception was encoutered:");
			Debug.WriteLine(e.ToString());
			Debug.WriteLine("APPLICATION STARTER: Shutting down");

			MessageBox.Show( String.Format( CultureInfo.CurrentCulture, "There was an error when trying to start the target application: {0}", e.Message ) );			
			Environment.Exit(0);
		}
		#endregion

		private static void updaterManager_PendingUpdatesDetected(object sender, PendingUpdatesDetectedEventArgs e) {			
			ApplicationUpdaterManager updaterManager = sender as ApplicationUpdaterManager;
			updaterManager.ResumePendingUpdates();
		}
	}
}
